﻿using System;
using System.Text;
using System.Security.Cryptography;
using System.IO;

namespace Nb.Common.Encrypt
{
    /// <summary>
    /// 非对称加密场景中用到的
    /// 数字签名助手类
    /// 其中非对称加密默认算法是
    /// RSA，哈希算法默认是SHA1
    /// 字符串编码默认Base64
    /// </summary>
    public class MySignatureHelper
    {
        #region 私有字段

        private static readonly string theBlobString = "BwIAAACkAABSU0EyAAQAAAEAAQCFv6TyuNMCnD5/1/UYO6frz/+db08Sc+5lbmPFdLjt4NFrD4prXuKhZQbWU+U2f245TTpMfZd21dM14DT2fLhU54EPoiDBVRjpeETNXY2u9u0THw1dPfgC5agz03Yfwb+MUQFfOdURM/2pe+FWD9vITkzFkGZ5PaB77ww0/7hvnqUKnooNjh4W9RwOocytavJKM3rUSDdTprzysiHtzUrU1OADWl+4c4S6hVFxsVr2JuG0sbvbJDGQz4Mg26W1jNlhK8Xw/vGzxJR8oHSQEvNBO/uwHtO1Xy81sodSLW9wvGdGTJ8y+zbsaGZNjq1QtD3l0CyEAPEouEWZH2WAXXC6gdleSs1j4uHCMoT2P06z2/fstqaQUPq9EXTMUw4t5eTEmlOPVs2RvaNA/d/uV8dG2mQhXdjueanqriSvJnJvh0GigzYp6QRX2KBDkSsW//gZiBR4UVbPlzzlvNNdXm0peE6REkMF7j6cKn4vYQ1GaD3urbzrzuax35gW/2+6dx6FJXuO6Ds6rM7PZSExaEY4GATFhwuEPYxsfXDRj7sYrZTpUArj///o3AvAsbWiHMRIMHSb759OTZac5Do/KCnMARJveSnD2ygFj4ZbZKYM3J58s6QQi4GASaAnSBTSVyFtmAOjKnHzZDZeuRMZv32HDnoG53BKlUxE85VofBNae1gCdNPyQjKsn1DsOaj1kTVvuTcmcenQmiaYzVWpr9fsceIPesnlM6XmbPmaHrgNA1/htFzYQl7toz15p7ZeF08=";
        private RSACryptoServiceProvider _rsa;//签名用的非对称加密算法
        private HashAlgorithm _hashAlgorithm;//签名用的哈希算法

        #endregion

        #region 属性

        public RSACryptoServiceProvider Rsa
        {
            get { return _rsa; }
            set { _rsa = value; }
        }

        public HashAlgorithm HashAlgorithm
        {
            get { return _hashAlgorithm; }
            set { _hashAlgorithm = value; }
        }

        #endregion

        private static readonly object _instanceLock = new object();
        private static MySignatureHelper _instance = null;
        public static MySignatureHelper Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_instanceLock)
                    {
                        if (_instance == null)
                        {
                            _instance = new MySignatureHelper();
                        }
                    }
                }
                return _instance;
            }
        }

        private static RSACryptoServiceProvider _rsaAlgorithmInstance = new RSACryptoServiceProvider();

        #region 构造函数

        //签名用的帮助类，提供默认的非对称算法和哈希算法RSACryptoServiceProvider和SHA1CryptoServiceProvider
        /// <summary>
        /// 签名用的帮助类，提供默认的非对称算法和哈希算法RSACryptoServiceProvider和SHA1CryptoServiceProvider
        /// </summary>
        private MySignatureHelper()
            : this(_rsaAlgorithmInstance, new SHA1CryptoServiceProvider())
        {
            //单例使用时，随机的值，会导致加密不一致
            //固定私密钥，保证不是随机的值，避免每次加密的值不一样
            byte[] blob = Convert.FromBase64String(theBlobString);
            _rsaAlgorithmInstance.ImportCspBlob(blob);
        }

        private MySignatureHelper(RSACryptoServiceProvider rsa) : this(rsa, new SHA1CryptoServiceProvider()) { }

        //签名用的帮助类，可以自行指定加非对称算法和哈希算法
        /// <summary>
        /// 签名用的帮助类，可以自行指定加非对称算法和哈希算法
        /// </summary>
        /// <param name="rsa">签名用的非对称加密算法</param>
        /// <param name="hashAlgorithm">签名用的哈希算法</param>
        private MySignatureHelper(RSACryptoServiceProvider rsa, HashAlgorithm hashAlgorithm)
        {
            _rsa = rsa;
            _hashAlgorithm = hashAlgorithm;
        }

        #endregion

        #region 公开方法

        //数字签名方法，以字节为目标和源
        /// <summary>
        /// 数字签名方法，以字节为目标和源
        /// </summary>
        /// <param name="inputBytes">待签字的字节输入源</param>
        /// <returns>以字节形式返回的数字签名</returns>
        public byte[] SignData(byte[] inputBytes)
        {
            return _rsa.SignData(inputBytes, _hashAlgorithm);
        }

        //数字签名方法，以字符串为目标和源
        /// <summary>
        /// 数字签名方法，以字符串为目标和源
        /// </summary>
        /// <param name="inputBytes">待签字的字符串输入源</param>
        /// <returns>以字符串形式返回的数字签名</returns>
        public string SignData(string inputString)
        {
            byte[] inputBytes = Encoding.Unicode.GetBytes(inputString);
            byte[] signatureBytes = SignData(inputBytes);
            return Convert.ToBase64String(signatureBytes);
        }

        //数字签名校验，以字节为目标和源
        /// <summary>
        /// 数字签名校验，以字节为目标和源
        /// </summary>
        /// <param name="inputBytes">待校验的字节输入源</param>
        /// <param name="signatureBytes">以字节形式返回的数字签名</param>
        /// <returns>是否一致</returns>
        public bool VerifyData(byte[] inputBytes, byte[] signatureBytes)
        {
            return _rsa.VerifyData(inputBytes, _hashAlgorithm, signatureBytes);
        }

        //数字签名校验，以字符串为目标和源
        /// <summary>
        /// 数字签名校验，以字符串为目标和源
        /// </summary>
        /// <param name="inputBytes">待校验的字符串输入源</param>
        /// <param name="signatureBytes">以字符串形式返回的数字签名</param>
        /// <returns>是否一致</returns>
        public bool VerifyData(string inputString, string signatureString)
        {
            bool result = false;
            try
            {
                byte[] inputBytes = Encoding.Unicode.GetBytes(inputString);
                byte[] signatureBytes = Convert.FromBase64String(signatureString);
                result = VerifyData(inputBytes, signatureBytes);
            }
            catch (Exception ex)
            {
                string temp = ex.Message;
            }
            return result;
        }

        //数字签名方法，以byte[]返回，以filePath输入
        /// <summary>
        /// 数字签名方法，以byte[]返回，以filePath输入
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <returns>byte[]类型的数字签名</returns>
        public byte[] SignFileDataReturnBytes(string filePath)
        {
            byte[] returnBytes = null;
            bool isExist = File.Exists(filePath);
            if (isExist)//如果存在
            {
                byte[] inputBytes;
                try
                {
                    inputBytes = File.ReadAllBytes(filePath);
                }
                catch (Exception ex)
                {
                    throw new ApplicationException(ex.Message + " 读取文件内容出现异常误");//抛出自定义的异常
                }

                try
                {
                    returnBytes = SignData(inputBytes);
                }
                catch (Exception ex)
                {
                    throw new ApplicationException(ex.Message + " 签名过程出现异常");//抛出自定义的异常
                }
            }
            else
            {
                throw new ApplicationException("没有找到指定的文件，导致异常");
            }
            return returnBytes;
        }

        //数字签名方法，以string返回，以filePath输入
        /// <summary>
        /// 数字签名方法，以string返回，以filePath输入
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <returns>base64编码的string类型的数字签名</returns>
        public string SignFileDataReturnString(string filePath)
        {
            return Convert.ToBase64String(SignFileDataReturnBytes(filePath));
        }

        //数字签名校验，以文件路径和byte[]目标和源
        /// <summary>
        /// 数字签名校验，以文件目标和源
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="signatureBytes">签名字节</param>
        /// <returns>是否校验成功</returns>
        public bool VerifyFileData(string filePath, byte[] signatureBytes)
        {
            byte[] returnBytes = null;
            bool isExist = File.Exists(filePath);
            if (isExist)//如果存在
            {
                byte[] inputBytes;
                try
                {
                    inputBytes = File.ReadAllBytes(filePath);
                }
                catch (Exception ex)
                {
                    throw new ApplicationException(ex.Message + " 读取文件内容出现异常误");//抛出自定义的异常
                }

                try
                {
                    returnBytes = SignData(inputBytes);
                }
                catch (Exception ex)
                {
                    throw new ApplicationException(ex.Message + " 签名过程出现异常");//抛出自定义的异常
                }
            }
            else
            {
                throw new ApplicationException("没有找到指定的文件，导致异常");
            }
            return VerifyData(returnBytes, signatureBytes);
        }

        //数字签名校验，以文件路径和string签名为目标和源
        /// <summary>
        /// 数字签名校验，以文件路径和string签名为目标和源
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="signatureString">base64格式的签名string</param>
        /// <returns>是否校验成功</returns>
        public bool VerifyFileData(string filePath, string signatureString)
        {
            byte[] signatureBytes = Convert.FromBase64String(signatureString);
            return VerifyFileData(filePath, signatureBytes);
        }

        #endregion

        #region 公开静态方法

        //数字签名用
        public static string SignByPrivateKey(string inputString, string xmlPrivateKey)
        {
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            rsa.FromXmlString(xmlPrivateKey);
            MySignatureHelper signatureHelper = new MySignatureHelper(rsa);
            return signatureHelper.SignData(inputString);
        }

        //校验数字签名用
        public static bool VerifyByPublicKey(string inputString, string xmlPublicKey, string signature)
        {
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            rsa.FromXmlString(xmlPublicKey);
            MySignatureHelper signatureHelper = new MySignatureHelper(rsa);
            return signatureHelper.VerifyData(inputString, signature);
        }

        #endregion
    }
}
